﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Week1.world.terrain;

namespace Week1.world.behavior
{
    class BehaviorPath : BehaviorBase
    {
        //TODO: add loop parameter for cycle moves again and again
        //TODO: detect turn (up down, left right). This will be used to animate objects
        //TODO: Implement width parameter so that objects can offset themselves within the cell or across multiple cells
        // TODO: display path debug. WIP

        #region variables

        private long[][] lStartTime;

        /// <summary>
        /// Path is unique per object type. e.g, land objects will follow a path, vs flying objects vs sea, etc
        /// 2nd dimension is not for instance. it is for path array.
        /// </summary>
        private Vector3[][] oPath;

        private struct Path
        {
            public Vector3[] Start;
            public Vector3[] End;
        };
        Path[] oPath2;


        private int[][] iMyCurrentPathIdx;

        private Vector3[][] oMyDirection;

        /// <summary>
        /// Ignored. it is alwasy 1.
        /// </summary>
        private float[][] fSpeed;

        //TODO: Why is this variable used? 
        private static BehaviorPath instance;

        private bool[][] bStarted;

        #endregion

        private BehaviorPath()
            : base()
        {
            oPath = new Vector3[MAXREFERENCES][];
            oPath2 = new Path[MAXREFERENCES];
            fSpeed = new float[MAXREFERENCES][];
            iMyCurrentPathIdx = new int[MAXREFERENCES][];
            oMyDirection = new Vector3[MAXREFERENCES][];
            lStartTime = new long[MAXREFERENCES][];
            bStarted = new bool[MAXREFERENCES][];
            fSpeed = new float[MAXREFERENCES][];
        }

        public static BehaviorPath Instance
        {
            get
            {
                if (instance == null)
                    instance = new BehaviorPath();

                return instance;
            }
        }

        public override int getBehaviorHandlerID(DrawableGlobalVar oVars)
        {
            int handlerId = base.getBehaviorHandlerID(oVars);

            //oPath[handlerId] = new Vector3[MAXPATH];
            //fSpeed[handlerId] = new float[MAXPATH];
            iMyCurrentPathIdx[handlerId] = new int[oVars.iInstanceCount];
            oMyDirection[handlerId] = new Vector3[oVars.iInstanceCount];
            lStartTime[handlerId] = new long[oVars.iInstanceCount];
            bStarted[handlerId] = new bool[oVars.iInstanceCount];
            fSpeed[handlerId] = new float[oVars.iInstanceCount];
            return handlerId;
        }

        /// <summary>
        /// ignored
        /// </summary>
        /// <param name="handlerid"></param>
        /// <param name="oVars"></param>
        public override void initialize(int handlerid, DrawableGlobalVar oVars)
        {
        }

        public override void execute(int handlerid, DrawableGlobalVar oVars, GameTime gameTime)
        {
            if (isTriggerActive(handlerid))
            {
                for (int iDx = 0; iDx < oVars.iInstanceCount; iDx++)
                {
                    int myCurrentPathIdx = iMyCurrentPathIdx[handlerid][iDx];
                    int myNextPathIdx = myCurrentPathIdx + 1 < oPath[handlerid].Length ? myCurrentPathIdx + 1 : myCurrentPathIdx;

                    // is it my time or starting for the first time?
                    if ((gameTime.TotalGameTime.Seconds >= lStartTime[handlerid][iDx]) || bStarted[handlerid][iDx])
                    {
                        // no longer a newbie
                        bStarted[handlerid][iDx] = true;

                        // am i just starting
                        if (myCurrentPathIdx == 0)
                        {
                            // put me on first path point
                            oVars.oPosition[iDx] = oPath[handlerid][myCurrentPathIdx];
                            oVars.oWorld[iDx] = Matrix.CreateWorld(oVars.oPosition[iDx], Vector3.Forward, Vector3.Up);
                        }

                        // is my position equals my current path. if so get my direction
                        if (unchecked(Math.Round(oVars.oPosition[iDx].X)) == oPath[handlerid][myCurrentPathIdx].X
                            && unchecked(Math.Round(oVars.oPosition[iDx].Y)) == oPath[handlerid][myCurrentPathIdx].Y
                            && unchecked(Math.Round(oVars.oPosition[iDx].Z)) == oPath[handlerid][myCurrentPathIdx].Z)
                        {
                            // get my direction against next path
                            oMyDirection[handlerid][iDx] = (Vector3.Normalize(-oVars.oPosition[iDx] +
                                oPath[handlerid][myNextPathIdx]));
                            // now target next path
                            if (oPath[handlerid].Length > myCurrentPathIdx + 1)
                                iMyCurrentPathIdx[handlerid][iDx]++;
                            else
                                oMyDirection[handlerid][iDx] = Vector3.Zero;

                        }

                        // move me towards the destination
                        oVars.oPosition[iDx] += (fSpeed[handlerid][iDx] * oMyDirection[handlerid][iDx]);

                        oVars.oWorld[iDx].M41 = oVars.oPosition[iDx].X;
                        oVars.oWorld[iDx].M42 = oVars.oPosition[iDx].Y;
                        oVars.oWorld[iDx].M43 = oVars.oPosition[iDx].Z;

                    }
                }
            }

        }

        /// <summary>
        /// sets path points.
        /// </summary>
        /// <param name="handler"></param>
        /// <param name="instanceIndex">ignored</param>
        /// <param name="name">ignored</param>
        /// <param name="value">array of path points</param>
        public override void setBehaviorProperty(int handler, int instanceIndex, string name, Vector3[] value)
        {
            oPath[handler] = value;
        }

        /// <summary>
        /// sets starting point as seconds
        /// </summary>
        /// <param name="handler"></param>
        /// <param name="instanceIndex">ignored</param>
        /// <param name="name">ignored</param>
        /// <param name="value">start time in seconds</param>
        public override void setBehaviorProperty(int handler, int instanceIndex, string name, long[] value)
        {
            lStartTime[handler] = value;
        }

        /// <summary>
        /// Sets same speed for all instances
        /// </summary>
        /// <param name="handler"></param>
        /// <param name="instanceIndex"></param>
        /// <param name="name"></param>
        /// <param name="value"></param>
        public override void setBehaviorProperty(int handler, int instanceIndex, string name, float value)
        {
            for (int idx = 0; idx < fSpeed[handler].Length; idx++)
                fSpeed[handler][idx] = value;
        }

        /// <summary>
        /// set path.
        /// value parameter contains cell index instead of actual x,y coordinates. 
        /// Method converts the x,y cell index to actual x,y world coordinates
        /// </summary>
        /// <param name="handler"></param>
        /// <param name="instanceIndex"></param>
        /// <param name="name"></param>
        /// <param name="value">gamegrid cell index</param>
        public override void setBehaviorProperty(int handler, int instanceIndex, string name, Vector2[] value)
        {

            #region document
            /** get first path
             * get second path
             * subtract paths
             * adjust starting point as follows:
             * (firstpath.x - secondpath.x) / 2 = - 5 for (5 - 15). X
             * (firstpath.y - secondpath.y) / 2 = - 5 for (5 - 15). Y
             * Z simply determined by ramp type
            **/
            #endregion
            
            Vector3 prev;
            Vector3 next;
            Vector3 result;
            GameGrid grid = GameGrid.Instance;

            prev = grid.getCellCenteredCoordinates((int)value[0].X, (int)value[0].Y);
            next = grid.getCellCenteredCoordinates((int)value[1].X, (int)value[1].Y);
            result = (prev - next) /2;
            oPath2[handler].Start = new Vector3[value.Length];
            oPath2[handler].End = new Vector3[value.Length];
            oPath2[handler].Start[0] = prev + result;
            oPath2[handler].End[0] = prev - result;

            for (int idx = 1; idx < value.Length - 1; idx++)
            {
                prev = grid.getCellCenteredCoordinates((int)value[idx].X, (int)value[idx].Y);
                next = grid.getCellCenteredCoordinates((int)value[idx + 1].X, (int)value[idx + 1].Y);
                result = (prev - next) / 2;
                oPath2[handler].Start[idx] = prev + result;
                oPath2[handler].End[idx] = prev - result;
            }

        }
    }
}